#include "types.h"
#include <iostream>

using namespace std;

namespace Algo
{

const uint THRESHOLD_MIN = 2;
typedef std::map<uint, GlobLineId> tUniqueStringContainer;


void FillSimpleResData(ResultData& resData)
{
	Block block1;
    block1.first = 0;
    block1.second = 0;

    Block block2;
    block2.first = 1;
    block2.second = 0;

    EqualBlocks eqBlocks;
    eqBlocks.push_back(block1);
    eqBlocks.push_back(block2);

    GroupOfBlocks group1;
    group1.push_back(eqBlocks);

    resData.insert(std::make_pair(10, group1));

	Block block3;
    block3.first = 1;
    block3.second = 4;

    Block block4;
    block4.first = 2;
    block4.second = 6;

    EqualBlocks eqBlocks2;
    eqBlocks2.push_back(block3);
    eqBlocks2.push_back(block4);

    GroupOfBlocks group2;
    group2.push_back(eqBlocks2);

    resData.insert(std::make_pair(5, group2));
}


void FillSampleIncomingData(IncomingData& incData)
{
	//  Create  simple data files
	FileData file1;
	for (uint i = 0; i < 6; ++i)
	{
		file1.push_back(i);
	}
	file1.push_back(0);
	file1.push_back(0);
	for (uint i = 0; i < 6; ++i)
	{
		file1.push_back(i);
	}

	incData.push_back(file1);
	//*
	FileData file2;
	for (uint i = 0; i < 6; ++i)
	{
		file2.push_back(i);
	}
	/*	if uncomment not worked
	for (uint i = 0; i < 6; ++i)
	{
	file2.push_back(i);
	}
	*/
	incData.push_back(file2);
	//*/
}


void PrintIncData(const IncomingData& incData)
{
	for (uint iFile = 0; iFile < incData.size(); ++iFile)
	{
		cout << "file index: " << iFile << endl;
		for (uint iLine = 0; iLine < incData[iFile].size(); ++iLine)
		{
			cout << iLine << ": " << incData[iFile][iLine];
			cout << endl;
		}
		cout << endl;
	}
}

struct MyBlock
{
	std::vector<MyPart> copies;
	std::vector<GlobLineId> orig;
	std::vector<GlobLineId> copy;
	uint threshold;
};


void AddBlock(MyBlock* block, ResultData& resData)
{
	const uint threshold = block->orig.size();
	if (threshold < THRESHOLD_MIN)
		return;
	const GlobLineId origPos = block->orig.front();
	const GlobLineId copyPos = block->copy.front();
	
	EqualBlocks eqBlock;
	eqBlock.push_back(origPos);
	eqBlock.push_back(copyPos);

	ResultData::iterator itGroupOfEq = resData.find(threshold);
	if (itGroupOfEq == resData.end())
	{
		GroupOfBlocks group;
		group.push_back(eqBlock);
		resData.insert(make_pair(threshold, group));
	}
	else
	{
		GroupOfBlocks::iterator itEqBlock = itGroupOfEq->second.begin();
		GroupOfBlocks::iterator itEqBlockEnd = itGroupOfEq->second.end();
		for ( ; itEqBlock != itEqBlockEnd; ++itEqBlock)
		{
			for ( EqualBlocks::iterator itBlock = itEqBlock->begin(); itBlock != itEqBlock->end(); ++itBlock)
			{
				if (*itBlock == origPos)
				{
					itEqBlock->push_back(copyPos);
					return;
				}
			}
		}

		itGroupOfEq->second.push_back(eqBlock);
	}
}


void FindEqualBlocks(const IncomingData& incData, ResultData& resData)
{
	tUniqueStringContainer uniqLines;

	std::size_t szFiles = incData.size();
	for (uint iFilePos = 0 ; iFilePos < szFiles ; ++iFilePos)				//	By files
	{
		bool bRepeatBlock = false;

		MyBlock* curBlock = NULL;

		GlobLineId posSavedOrigLine;

		const FileData& vCurLines = incData[iFilePos];
		std::size_t szLines = incData[iFilePos].size();
		for (uint iLinePos = 0; iLinePos < szLines; ++iLinePos)				//	By lines
		{
			uint CurLine = vCurLines[iLinePos];

			tUniqueStringContainer::iterator itUniqLine = uniqLines.find(CurLine);
			if (itUniqLine != uniqLines.end())
			{
				GlobLineId posFindedOrigLine = itUniqLine->second;
				
				if (!bRepeatBlock)		//	start new block
				{
					bRepeatBlock = true;
					curBlock = new MyBlock;
				}
				
				//	Compare old orig position with new finded orig position
				if (posSavedOrigLine != posFindedOrigLine)
				{
					AddBlock(curBlock, resData);
					delete curBlock;
					curBlock = new MyBlock;
					posSavedOrigLine = posFindedOrigLine;
				}

				curBlock->orig.push_back(itUniqLine->second);
				curBlock->copy.push_back(make_pair(iFilePos, iLinePos));

				posSavedOrigLine.second++;
			}
			else
			{
				uniqLines.insert(make_pair(CurLine, make_pair(iFilePos, iLinePos)));
				if (bRepeatBlock)
				{
					AddBlock(curBlock, resData);
					delete curBlock; curBlock = NULL;
					bRepeatBlock = false;
				}
			}
		}

		if (bRepeatBlock)
		{
			AddBlock(curBlock, resData);
			delete curBlock; curBlock = NULL;
			bRepeatBlock = false;
		}
	}

}


void TestFindEqualBlocks(const IncomingData& incData, ResultData& resData)
{
	IncomingData myIncData;
	FillSampleIncomingData(myIncData);

	PrintIncData(myIncData);
	//FillSimpleResData(resData);

	FindEqualBlocks(myIncData, resData);
}


}	//	namespace Algo